// Copyright 2008 Senary
// See credits.txt for details.

// This file is part of Omega VII.

// Omega VII is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Omega VII is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Omega VII.  If not, see <http://www.gnu.org/licenses/>.

#include <cstring>
#include <sstream>

#include "Level.h"
#include "GameManager.h"
#include "Player.h"
#include "AIPlayer.h"
#include "Star.h"
#include "MediaManager.h"
#include "Camera.h"

using namespace std;


// copies the orbital target from the entity (loaded from a level)
// to the object.
void Level::readOrbitalTarget(SpaceObject* so, ENTITY const* ent) const {
	GameManager* gm = GameManager::getInstance();
	
	SpaceObject* orbitalTarget = 0;
	if (ent->string2 != 0 && string(ent->string2) != "") {
		ENTITY* orbitalEntity = ent_for_name(ent->string2);
		orbitalTarget = gm->findObject(orbitalEntity);
	}
	if (orbitalTarget != 0) {
		// Orbital time is stored in skill2
		float orbitalTime = _FLOAT(ent->skill[1]);
		if (orbitalTime == 0) {
			orbitalTime = (float)SHIP_ORBITING_TIME;
		}

		so->setOrbitalTime(orbitalTime);
		so->startOrbit(orbitalTarget, 0.0f);
	}

}

// sets the player pointer for each loaded entity
void Level::readPlayer(SpaceObject* so, ENTITY const* ent) const {
	GameManager* gm = GameManager::getInstance();
	
	int playerNo = _INT(ent->skill[0]);
	if (playerNo >= 0 && playerNo < (signed)(gm->getPlayers().size())) {
		so->setOwner(gm->getPlayers()[playerNo]);

		// Have to use RTTI here... sorry ;-)
		Planet* p = dynamic_cast<Planet*>(so);
		Spaceship* s = dynamic_cast<Spaceship*>(so);
		if (p != 0) {
			gm->getPlayers()[playerNo]->addPlanet(p);
		} else if (s != 0) {
			gm->getPlayers()[playerNo]->addSpaceship(s);
		}

	} else if (playerNo == -1) {
		so->setOwner(0);
	} else {
		throw string("Illegal player id (skill1) given!");
	}
}

Level::Level(void)
{
}

Level::~Level(void)
{
}

void Level::initialize(string const& levelFilename)
{
	GameManager* gm = GameManager::getInstance();

	gm->reset();

	// Grrr GameStudio, why don't you take a /const/ char *
	char* tempLevelFilename = _strdup(levelFilename.c_str());
	level_load(tempLevelFilename);
	// Wait three frames for level to load & display
	engine_frame(); engine_frame(); engine_frame();
	free(tempLevelFilename);

	// Background music
	SOUND* backgrd = MediaManager::getInstance()->getSound("09 - Dj Fab - Through the Shadow and the Light (Feat. Fahrenheit).ogg");
	snd_loop(backgrd,_VAR(100),_VAR(0));

	// Player 0 will always be _the_ human player. There might be several AI players.
	// We'll check the highest player id found in the level and create enough players accordingly.
	int maxPlayerNum = -1;

	// Retrieve all the entities from the level and create
	// the corresponding objects.

	ENTITY* curEnt = 0;
	while ((curEnt = ent_next(curEnt)) != 0) {
		if (curEnt->string1 != 0 && string(curEnt->string1) != "") {
			// string1 has the format "ObjectType ..."
			istringstream iss;
			iss.str(string(curEnt->string1));
			string objectType;
			iss >> objectType;

			if (objectType == "Planet") {
				string objectName;
				iss >> objectName;
				Planet* p = new Planet(Planet::planetTypeFromString(objectName));
				
				p->setPosition(Vector((VECTOR*)(&(curEnt->x))));
				gm->addPlanet(p, curEnt);
				if (objectName == "OmegaVii") {
					gm->getCamera()->setOrigin(p->getPosition());
					gm->getCamera()->setPosition(p->getPosition() + Vector(0.42 * UNIVERSE_SIZE.x, 0.0, 0.0));
					gm->getCamera()->setBoundary(10.0 * AU_IN_QUANTS, 0.42 * UNIVERSE_SIZE.x);
					Star::createStar(p);
					curEnt->flags |= INVISIBLE;
				}
			}
			
			if (objectType == "Spaceship") {
				string objectName;
				iss >> objectName;
				Spaceship* s = new Spaceship(Spaceship::spaceshipTypeFromString(objectName));
				
				s->setPosition(Vector((VECTOR*)(&(curEnt->x))));
				s->setAngle(Vector(Angle((ANGLE*)(&(curEnt->pan)))));
				gm->addSpaceship(s, curEnt);
			}

			if (objectType == "Planet" || objectType == "Spaceship") {
				if (_INT(curEnt->skill[0]) > maxPlayerNum) {
					maxPlayerNum = _INT(curEnt->skill[0]);
				}
			}

			// Set entities to dynamic to allow movement
			curEnt->emask |= DYNAMIC;
		}
	}

	// create the players.
	// Human player
	Player* hpl = new Player;
	hpl->setName("your Excellence");
	hpl->setIsHumanPlayer(true);						
	gm->addPlayer(hpl);

	// Computer players
	for (int i = 1; i <= maxPlayerNum; ++i) {
		AIPlayer* pl2 = new AIPlayer;
		ostringstream oss;
		oss << "Player " << i;
		pl2->setName(oss.str());
		pl2->setIsHumanPlayer(false);
		gm->addPlayer(pl2);
	}

	// If the object is orbiting, the orbital target is stored
	// in string2. Thus we check all objects and update their orbitalTarget
	// according to their string2. We can't do this earlier because all
	// objects have to be loaded first.
	// Additionally, we set the player pointer for each object. This also
	// can't be done earlier because the players have to be loaded first.
	curEnt = 0;
	while ((curEnt = ent_next(curEnt)) != 0) {
		SpaceObject* so = gm->findObject(curEnt);
		if (so != 0) {
			readOrbitalTarget(so, curEnt);
			readPlayer(so, curEnt);
		}
	}
}

// Returns true if the game is over
// param: the list of objects from the game manager
// FIXME: this is not elegant...
bool Level::isGameOver()
{
	int nLivingPlayers = 0;
	const std::vector<Player*> & players = GameManager::getInstance()->getPlayers();

	// we return true if all objects belong to the same player
	for (unsigned int i = 0; i < players.size(); ++i) {
		if (players[i]->getPlanets().size() > 0) {
				++nLivingPlayers;
		}
	}

	return (nLivingPlayers <= 1);
}

// Returns true if the human player won
// param: the list of objects from the game manager
// FIXME: this is not elegant...
bool Level::isVictory()
{
	// we return true if the game is over and the human is still alive
	if (!isGameOver()) {
		return false;
	}

	Player* humanPlayer = GameManager::getInstance()->getPlayers()[0];

	if (humanPlayer->getSpaceships().size() > 0
		|| humanPlayer->getPlanets().size() > 0) {
			return true;
	} else {
		return false;
	}
}
